package com.eden.api.security.web;

import com.eden.api.security.JwtUser;
import com.eden.api.security.annotation.AnonymousAccess;
import com.eden.api.security.service.ITokenService;
import com.eden.common.utils.reflect.ReflectUtils;
import com.eden.common.utils.spring.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * @description: fa
 * @author: wenqing
 * @date: 2019/11/16 22:10
 */
@Slf4j
@Component
public class SpringContextListener implements ApplicationListener<ContextRefreshedEvent>, Ordered {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {

        JwtUser jwtUser = JwtUser.getInstance();
        ITokenService tokenService = SpringUtils.getBean(ITokenService.class);
        ReflectUtils.setFieldValue(jwtUser, "tokenService", tokenService);

        Map<String, Object> beanMap = contextRefreshedEvent.getApplicationContext().getBeansWithAnnotation(Controller.class);
        for (String key : beanMap.keySet()) {
            Object bean = beanMap.get(key);
            Object target = getTarget(bean);
            if(target == null){
                continue;
            }
            Class clazz = target.getClass();
            Method[] methods = clazz.getDeclaredMethods();
            if (methods == null || methods.length == 0) {
                continue;
            }

            for (Method method : methods) {
                if (method.isAnnotationPresent(AnonymousAccess.class)) {
                    //AnonymousAccess annotation = method.getAnnotation(AnonymousAccess.class);
                    log.info("方法"+clazz.getName()+"."+method.getName()+"有@AnonymousAccess注解");
                }
            }
        }
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

    public static final <T> T getTargetFromProxy(T proxyObject) {
        Class<?> clazz = proxyObject.getClass();
        if(clazz.getName().startsWith("$Proxy")) {
            try {
                clazz = clazz.getSuperclass();
                Field hField = clazz.getDeclaredField("h");
                hField.setAccessible(true);
                Object hObject = hField.get(proxyObject);

                Class<?> dynamicProxyClass = hObject.getClass();
                Field advisedField = dynamicProxyClass.getDeclaredField("advised");
                advisedField.setAccessible(true);
                Object advisedObject = advisedField.get(hObject);

                Class<?> advisedSupportClass = advisedObject.getClass().getSuperclass().getSuperclass();
                Field targetField = advisedSupportClass.getDeclaredField("targetSource");
                targetField.setAccessible(true);
                Object targetObject = targetField.get(advisedObject);

                Class<?> targetSourceClass = targetObject.getClass();
                Field targetClassField = targetSourceClass.getDeclaredField("target");
                targetClassField.setAccessible(true);
                return (T) targetClassField.get(targetObject);
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return null;
    }


    public static Object getTarget(Object beanInstance) {
        if (!AopUtils.isAopProxy(beanInstance)) {
            return beanInstance;
        } else if (AopUtils.isCglibProxy(beanInstance)) {
            try {
                Field h = beanInstance.getClass().getDeclaredField("CGLIB$CALLBACK_0");
                h.setAccessible(true);
                Object dynamicAdvisedInterceptor = h.get(beanInstance);

                Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
                advised.setAccessible(true);

                Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
                return target;
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
        return null;

    }
}
